﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.ServiceModel;

namespace SkypeMonitorService
{
	public partial class SkypeMonitorService : ServiceBase
	{
		private static ILog Logger = null;
		private volatile bool m_isFirstTime = true;
		Task m_monitorTask = null;
		Task m_hostStarter = null;
		System.Threading.CancellationTokenSource m_cts = null;
		private string pathToProgram = null;

		private bool needRunTranslater = false;

		private const String cstrScope = "root\\CIMV2";
		private const String cstrLoggenInUser = "SELECT * FROM Win32_ComputerSystem";

		private ServiceHost wcfServiceHost = null;

		public SkypeMonitorService()
			: base()
		{
			InitializeComponent();
			if (System.AppDomain.CurrentDomain != null)
				System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
			try
			{
				Logger = log4net.LogManager.GetLogger(typeof(SkypeMonitorService).FullName);
				log4net.Config.XmlConfigurator.Configure();

				this.Disposed += new EventHandler(SkypeMonitorService_Disposed);
				m_cts = new System.Threading.CancellationTokenSource();
				m_monitorTask = new Task(new Action(Monitor), m_cts.Token, TaskCreationOptions.LongRunning);
			}
			catch (Exception ex) { Logger.Fatal("ctor", ex); }

			this.needRunTranslater = System.Configuration.ConfigurationManager.AppSettings["RunTranslater"].ToLower().Contains("true");
			Logger.InfoFormat("RunTranslater: {0}", System.Configuration.ConfigurationManager.AppSettings["RunTranslater"]);
			this.pathToProgram = System.Configuration.ConfigurationManager.AppSettings["programPath"];

			if (pathToProgram.EndsWith(@"\\"))
				pathToProgram = pathToProgram.Remove(pathToProgram.Length - 1);

			Logger.Info("Program path: " + pathToProgram);
		}

		void wcfServiceHost_Faulted(object sender, EventArgs e)
		{
			Logger.Warn("Wcf service faulted");
		}

		void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
		{
			Logger.Fatal("Unhandled exception", e.ExceptionObject as Exception);
		}

		void SkypeMonitorService_Disposed(object sender, EventArgs e)
		{
			log4net.LogManager.Shutdown();
		}

		protected override void OnStart(string[] args)
		{
			Logger.Info("On start");
			m_monitorTask.Start();

			m_hostStarter = new Task(new Action(RunWcfService), TaskCreationOptions.PreferFairness);

			m_hostStarter.ContinueWith(task =>
			{
				if (task.Exception != null)
					Logger.Error("m_hostStarter fault", task.Exception);
				task.Dispose();
				task = null;
			}, TaskContinuationOptions.NotOnRanToCompletion);

			m_hostStarter.ContinueWith(task =>
			{
				Logger.Info("m_hostStarter. Wcf service was start successfully");
				if (wcfServiceHost != null)
					Logger.InfoFormat("m_hostStarter state: {0}", wcfServiceHost.State.ToString());
				task.Dispose();
				task = null;
			}, TaskContinuationOptions.OnlyOnRanToCompletion);

			m_hostStarter.Start();
		}

		private void RunWcfService()
		{
			wcfServiceHost = new ServiceHost(typeof(WcfTranslateLibrary.TranslateService));
			wcfServiceHost.Faulted += new EventHandler(wcfServiceHost_Faulted);
			Logger.Info("Try to open wcfServiceHost");

			wcfServiceHost.Open();
		}

		protected override void OnStop()
		{
			Logger.Info("On stop");
			if (m_monitorTask.Status == TaskStatus.Running)
			{
				m_cts.Cancel();
				m_cts.Dispose();
				m_cts = null;
				m_cts = new System.Threading.CancellationTokenSource();

				m_monitorTask = null;
				m_monitorTask = new Task(new Action(Monitor), m_cts.Token);
			}

			if (wcfServiceHost != null && wcfServiceHost.State == CommunicationState.Opened)
			{
				try
				{
					wcfServiceHost.Close();
					wcfServiceHost = null;
				}
				catch (Exception ex)
				{
					Logger.Error("Error occurred while closing wcf host", ex);
					if (wcfServiceHost != null)
					{
						wcfServiceHost.Abort();
						wcfServiceHost = null;
					}
				}
			}
			else if (wcfServiceHost != null && wcfServiceHost.State == CommunicationState.Faulted)
			{
				try
				{
					wcfServiceHost.Abort();
					wcfServiceHost = null;
				}
				catch (Exception ex)
				{ Logger.Error("Error occurred while closing faulted wcf host", ex); }
			}
		}

		protected override void OnShutdown()
		{
			if (System.AppDomain.CurrentDomain != null)
				System.AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

			Logger.Info("On shutdown");
			base.OnShutdown();

			if (m_monitorTask.Status == TaskStatus.Running && !m_cts.IsCancellationRequested)
			{
				m_cts.Cancel();
				m_cts.Dispose();
				m_cts = null;


				if (m_monitorTask != null)
				{
					m_monitorTask.Dispose();
					m_monitorTask = null;
				}
			}
		}

		private void Monitor()
		{
			while (true)
			{
				if (m_cts.IsCancellationRequested)
				{
					Logger.Info("Cancel requested");
					break;
				}

				if (m_isFirstTime)
				{
					//Logger.Info("Service monitor thread is first time invoked");
					m_isFirstTime = false;
					System.Threading.Thread.Sleep(11000);
				}

				if (System.Diagnostics.Process.GetProcesses().Any(x => (string.Compare(x.ProcessName, "Skype", true) == 0 && x.SessionId == 1)))
				{
					if (!System.Diagnostics.Process.GetProcesses().Any(x => (string.Compare(x.ProcessName, "SkypeTranslater", true) == 0 && x.SessionId == 1))
						&& needRunTranslater)
					{
						CreateProcessAsUserWrapper.LaunchChildProcess(System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"));

						IntPtr processToken = IntPtr.Zero;
						IntPtr processTokenDuplicated = IntPtr.Zero;
						try
						{
							Logger.Info(System.Security.Principal.WindowsIdentity.GetCurrent().Name);

							Process proc = Process.GetProcesses().FirstOrDefault(x => x.ProcessName.Contains("explorer"));
							if (proc != null)
							{
								IntPtr procHandle = proc.Handle;

								if (!NativeMethods.OpenProcessToken(procHandle, NativeMethods.TOKEN_ALL_ACCESS, out processToken))
								{
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
								}

								Win32.SECURITY_ATTRIBUTES sa = new Win32.SECURITY_ATTRIBUTES();
								sa.bInheritHandle = false;
								sa.nLength = System.Runtime.InteropServices.Marshal.SizeOf(sa);
								sa.lpSecurityDescriptor = (IntPtr)0;
								uint GENERIC_ALL = 0x10000000;

								System.Security.Principal.WindowsImpersonationContext contInernal = null;
								if (!NativeMethods.ImpersonateLoggedOnUser(processToken))
								{
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
								}

								Logger.Info(System.Security.Principal.WindowsIdentity.GetCurrent().Name);

								if (!Win32.DuplicateTokenEx(processToken, GENERIC_ALL, ref sa, Win32.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, 1, out processTokenDuplicated))
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

								Win32.PROCESS_INFORMATION processInfo = new Win32.PROCESS_INFORMATION();
								Win32.STARTUPINFO startInfo = new Win32.STARTUPINFO();
								startInfo.cb = System.Runtime.InteropServices.Marshal.SizeOf(startInfo);

								if (!Win32.CreateProcessAsUser(processTokenDuplicated, null, System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"),
																			IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startInfo, out processInfo))
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

								if (contInernal == null)
									NativeMethods.RevertToSelf();
								else
									contInernal.Undo();

								//Win32.WaitForSingleObject(processInfo.hProcess,-1);

								Win32.CloseHandle(processInfo.hProcess);
								Win32.CloseHandle(processInfo.hThread);
							}

						}
						catch (Exception ex) { Logger.Error("EX", ex); }
						finally
						{
							if (processToken != IntPtr.Zero)
								Win32.CloseHandle(processToken);
							if (processTokenDuplicated != IntPtr.Zero)
								Win32.CloseHandle(processTokenDuplicated);
						}

						try
						{
							string[] desks = UIHelper.GetDesktops();
							if (desks != null) { }

							bool isAdm = this.IsRunAsAdmin();
							if (isAdm) { }

							string deskName = desks.FirstOrDefault(x => x.Contains("connected"));
							Win32.LaunchCommand2OnDesktop(deskName, System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"), "EUROPEAN", "VIN26935", "OVK@#01071988");
						}
						catch (Exception ex) { Logger.Error("List sessions error", ex); }

						try
						{
							var sessions = TerminalServiceManager.ListSessions(Environment.MachineName);
							if (sessions != null)
							{
								//TerminalServiceManager.SendMessage(Environment.MachineName, "Hello from service", "Hello");
							}
						}
						catch (Exception ex) { Logger.Error("List sessions error", ex); }

						System.Security.Principal.WindowsImpersonationContext context = null;
						IntPtr hToken = IntPtr.Zero;
						IntPtr dupToken = IntPtr.Zero;
						try
						{
							bool bResult = false;
							uint sesId = RunAs.WTSGetActiveConsoleSessionId();

							uint uiResultWait;
							RunAs.WTSQueryUserToken(sesId, out hToken);
							if (hToken.ToInt32() > 0)
							{
								if (this.IsRunAsAdmin())
								{ }
								else
								{ }

								if (!NativeMethods.DuplicateToken(hToken, 2, out dupToken))
								{
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
								}

								Logger.Info("Before Impersonation: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);

								System.Security.Principal.WindowsIdentity identity = new System.Security.Principal.WindowsIdentity(hToken);
								context = identity.Impersonate();

								Logger.Info("After Impersonation: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);

								var process = System.Diagnostics.Process.Start(System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"));
								if (process.SessionId == 0)
								{
									process.Kill();
								}

								Win32.PROCESS_INFORMATION processInfo = new Win32.PROCESS_INFORMATION();
								Win32.STARTUPINFO startInfo = new Win32.STARTUPINFO();

								bResult = Win32.CreateProcessAsUser(hToken, null, System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"),
																	IntPtr.Zero, IntPtr.Zero,
																	false,
																	0,
																	IntPtr.Zero,
																	null,
																	ref startInfo, out processInfo);
								if (!bResult)
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

								// Wait for process to end
								uiResultWait = Win32.WaitForSingleObject(processInfo.hProcess, Win32.INFINITE);
								if (uiResultWait == Win32.WAIT_FAILED)
									throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
							}
						}
						catch (Exception ex) { Logger.Error("WINAPI", ex); }
						finally
						{
							if (hToken != IntPtr.Zero)
								Win32.CloseHandle(hToken);

							if (dupToken != IntPtr.Zero)
								Win32.CloseHandle(dupToken);

							if (context != null)
							{
								context.Undo();
								context.Dispose();
							}
							Logger.Info("After finnaly block : " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
						}

						try
						{
							string[] desk = Desktop.GetDesktops();
							if (desk != null)
							{
								var desk1 = Desktop.OpenDefaultDesktop();
								if (desk1 != null)
								{
									var col1 = desk1.GetWindows();
									if (col1 != null) { }
								}
								var inpDesk = Desktop.OpenInputDesktop();
								if (inpDesk != null)
								{

								}
							}
						}
						catch { }


						#region region2
						//using (ImpersonationUtil impers = new ImpersonationUtil())
						//{
						//    impers.ImpersonateUser("vin26935", "EUROPEAN", "OVK@#01071988");

						//    if (System.Diagnostics.Process.GetProcesses().Any(x => string.Compare(x.ProcessName, "Skype", true) == 0))
						//    {
						//        if (!System.Diagnostics.Process.GetProcesses().Any(x => string.Compare(x.ProcessName, "SkypeTranslater", true) == 0))
						//        {
						//            bool isExceptional = false;
						//            if (!string.IsNullOrEmpty(pathToProgram))
						//            {
						#region comment
						//                ProcessStartInfo info = new ProcessStartInfo();
						//                info.FileName = System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe");
						//                Logger.InfoFormat("Executable file: {0}", info.FileName);
						//                info.WorkingDirectory = System.IO.Path.GetDirectoryName(pathToProgram);
						//                info.UseShellExecute = false;
						//                info.CreateNoWindow = false;
						//                info.WindowStyle = ProcessWindowStyle.Normal;
						//                info.UserName = "vin26935";
						//                info.Domain = "EUROPEAN";
						//                var secPass = new System.Security.SecureString();
						//                foreach (char c in "OVK@#01071988".ToCharArray())
						//                    secPass.AppendChar(c);
						//                info.Password = secPass;

						//                bool procStarted = false;
						//                System.Diagnostics.Process process = new Process();
						//                process.StartInfo = info;
						//                try
						//                {
						//                    if (process.Start())
						//                    {
						//                        Logger.Info("Main window handle: " + process.MainWindowHandle.ToString());
						//                        Logger.Info("Start result is true");
						//                        procStarted = true;
						//                    }
						//                    else
						//                        Logger.Info("Start result is false");
						//                }
						//                catch (Exception ex)
						//                {
						//                    Logger.Error("Exception while trying start process", ex);
						//                    isExceptional = true;
						//                }
						//                finally
						//                {
						//                    if (procStarted)
						//                    {
						//                        try
						//                        {
						//                            UIHelper.BringWindowToTop("Skype message translater", true);
						//                        }
						//                        catch (System.ComponentModel.Win32Exception ex) { Logger.Error("Fail BringWindowToTop", ex); }
						//                    }
						//                    if (isExceptional)
						//                    {
						//                        Logger.Info("Process start throw an exception. Try to close process");
						//                        process.Kill();
						//                        process.Dispose();
						//                        Logger.Info("Process closed");
						//                    }
						//                    procStarted = false;
						//                }
						#endregion

						//                //try
						//                //{
						//                //    RunAs runner = new RunAs();
						//                //    runner.StartProcessAs(System.IO.Path.Combine(pathToProgram, "SkypeTranslater.exe"), System.IO.Path.GetDirectoryName(pathToProgram),
						//                //        "vin26935", "OVK@#01071988");
						//                //}
						//                //catch (Exception ex) { Logger.Error("StartProcessAs", ex);}
						//            }
						//        }
						//    }
						//} 
						#endregion
					}
				}
				System.Threading.Thread.Sleep(10000);
			}

		}

		private bool IsRunAsAdmin()
		{
			var Principle = new System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent());
			return Principle.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
		}

		/// <summary>
		/// Method returns name of the user that logged in on workstation
		/// </summary>
		private String GetLoggedInUserName()
		{
			String userName = String.Empty;

			try
			{
				System.Management.ManagementObjectSearcher searcher =
					new System.Management.ManagementObjectSearcher(cstrScope, cstrLoggenInUser);

				foreach (System.Management.ManagementObject queryObj in searcher.Get())
				{
					userName = queryObj["UserName"].ToString();
					break;
				}
			}
			catch
			{
				userName = String.Empty;
			}

			return userName;
		}

	}
}
